# math_functions.py
#
# Math functions that are not necessarily provided by the math package

import math
import unittest

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

def multiply(a, b):
    return a*b

def divide(a, b):
    return a/b

def average(list):
    return divide(reduce(add, list), len(list))

def heronsFormula(a, b, c):
    s = divide(a+b+c, 2.0)
    return math.sqrt(s*(s-a)*(s-b)*(s-c))

def heronsFormulaStable(a, b, c):
    sort_sides = [a, b, c]
    sort_sides.sort()
    c_stable = sort_sides[0]
    b_stable = sort_sides[1]
    a_stable = sort_sides[2]
    one_fourth = divide(1.0, 4.0)
    return one_fourth*math.sqrt((a_stable+(b_stable+c_stable))*(c_stable-(a_stable-b_stable))*(c_stable+(a_stable-b_stable))*(a_stable+(b_stable-c_stable)))

if __name__ == '__main__':
    class TestMathFunctions(unittest.TestCase):

        def test_add(self):
            self.assert_(add(1.0, 1.0) == 2.0)

        def test_subtract(self):
            self.assert_(subtract(0.0, 1.0) == -1.0)

        def test_multiply(self):
            self.assert_(multiply(2.0, 3.0) == 6.0)

        def test_divide(self):
            self.assert_(divide(3.0, 2.0) == 1.5)

        def test_average(self):
            self.assert_(average([1.0, 1.0, 1.0]) == 1.0)
            self.assert_(average([1.0, 2.0, 3.0]) == 2.0)

        def test_herons_formula(self):
            a = 3.0
            b = 4.0
            c = 5.0
            self.assert_(heronsFormula(a, b, c) == 6.0)
            self.assert_(heronsFormulaStable(a, b, c) == heronsFormula(a, b, c))

    unittest.main()
